/**
 *
 * 回行取数：解题思路
 *
 * 首先肯定需要一个int型变量direction,来存储当前取数的方向。依次可以设置为0：从上到下，
 * 1：从左到右，2：从下到上，3：从右到左（正好对应取数顺序转一圈）。
 *
 * 有了取数的方向，我们还需要某种方式记录矩阵中的数字有没有被取过。如果已经取过，则需要调换
 * 取数的方向。我们可以使用另一个二维数组selected来记录矩阵中每个对应位置的数是否被取过，
 * 1表示取过，0表示没有取过。
 *
 * 使用循环不断的取数，直到矩阵中所有数都被取过（所有数被取过的条件即已经取过的数的数量等于mn）。
 *
 * 算法伪代码
 *
 * INPUT: 二维矩阵input[m][n]
 * OUTPUT: 取数序列
 *
 *BEGIN
 * 获得m、n；
 * 从键盘读入二维数组input[m][n]；
 * num_selected := 0; // 已经取过的数字数量
 * 创建二维数组selected[m][n]，并将数组每一个元素设置为0。
 * 设置取数起始坐标 x := 0; y := 0;
 * 设置取数方向direction := 0；
 *
 * //使用循环进行取数
 * WHILE num_selected < m * n DO
 *  //选取坐标x,y的数;
 *  assert 0 <= x < m; // 应确保坐标是在范围内
 *  assert 0 <= y < n; // 应确保坐标是在范围内
 *  assert selected[x][y] == 0; // 当前位置应该没有被取过才是正常的
 *  输出input[x][y];（如果不是第一个输出的数，则前面补一个空格）
 *  selected[x][y] := 1;
 *  num_selected++;
 *  // 根据四个方向进行不同的处理判断是否需要变换方向
 *  IF direction == 0（向下） THEN
 *      IF x + 1 >= m OR selected[x+1][y] > 0 THEN
 *           向下如果已经超界或下面的数已经被取过，则右转
 *          direction := 1;
 *          y := y + 1;
 *      ELSE
 *           正常情况，向下
 *          x := x + 1;
 *      END
 *  ELSE IF direction == 1 （向右）THEN
 *      IF y + 1 >= n OR selected[x][y+1] > 0 THEN
 *           右边超界或右侧的数已经被取过，则向上
 *          direction := 2;
 *          x := x - 1;
 *      ELSE
 *           y := y + 1;
 *      END
 *  ELSE IF direction == 2（向上） THEN
 *      IF x - 1 < 0 OR selected[x-1][y] > 0 THEN
 *          direction := 3; // 向左
 *          y := y - 1;
 *      ELSE
 *          x := x - 1;
 *      END
 *  ELSE IF direction == 3 （向左）THEN
 *      IF y - 1 < 0 OR selected[x][y-1] > 0 THEN
 *          direction := 0;
 *          x := x + 1;
 *      ELSE
 *          y := y - 1;
 *      END (内层IF)
 *  END (外层IF)
 * END (WHILE)
 *END（算法）
 *
 * 只需将上述伪代码翻译成程序即可
 */

#include <asm-generic/errno.h>
#include <assert.h>
#include <stdio.h>

int main(void)
{
    // 获得m、n；
    int m = 0, n = 0;
    scanf("%d%d", &m, &n);
    // 从键盘读入二维数组input[m][n]；
    int input[200][200] = { 0 };
    int i, j;
    for (i = 0; i < m; i++) {
        for (j = 0; j < n; j++) {
            scanf("%d", &(input[i][j]));
        }
    }
    // num_selected := 0; // 已经取过的数字数量
    int num_selected = 0;
    // 创建二维数组selected[m][n]，并将数组每一个元素设置为0。
    int selected[200][200] = { 0 };
    // 设置取数起始坐标 x := 0; y := 0;
    int x = 0, y = 0;
    // 设置取数方向direction := 0；
    int direction = 0;
    // 使用循环进行取数
    //  WHILE num_selected < m * n DO
    while (num_selected < m * n) {
        // 选取坐标x,y的数;
        // assert 0 <= x < m; // 应确保坐标是在范围内
        // assert 0 <= y < n; // 应确保坐标是在范围内
        // assert selected[x][y] == 0; // 当前位置应该没有被取过才是正常的
        // printf("\n[x=%d, y=%d]\n", x, y);
        assert(x >= 0 && x < m);
        assert(y >= 0 && y < n);
        assert(selected[x][y] == 0);
        // 输出input[x][y];（如果不是第一个输出的数，则前面补一个空格）
        if (num_selected > 0) {
            printf(" ");
        }
        printf("%d", input[x][y]);
        selected[x][y] = 1;
        num_selected++;
        // 根据四个方向进行不同的处理判断是否需要变换方向
        if (direction == 0) { // 向下
            // IF x + 1 >= m OR input[x+1][y] > 0 THEN
            if ((x + 1 >= m) || (selected[x+1][y] > 0)) {
                direction = 1;
                y = y + 1;
            } else {
                x = x + 1;
            }
        } else if (direction == 1) { // 向右
            if ((y + 1 >= n) || (selected[x][y+1] > 0)) {
                direction = 2;
                x = x - 1;
            } else {
                y = y + 1;
            }
        } else if (direction == 2) { // 向上
            if ((x - 1 < 0) || (selected[x-1][y] > 0)) {
                direction = 3;
                y = y - 1;
            } else {
                x = x - 1;
            }
        } else if (direction == 3) { // 向左
            if ((y - 1 < 0) || (selected[x][y-1] > 0)) {
                direction = 0;
                x = x + 1;
            } else {
                y = y - 1;
            }
        }
    }
    return 0;
}
